package gk.gkapi.impl.db.hashmap;

import java.beans.IntrospectionException;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import gk.gkapi.DBMap;
import gk.gkapi.FilterParsingException;
import gk.gkapi.impl.filter.bean.Bean;
import gk.gkapi.impl.filter.bean.QueryBean;
import gk.gkapi.impl.filter.exp.FilterExpression;
import gk.gkapi.impl.filter.exp.FilterParser;

public class DBMapHashMapImpl<K, V> implements DBMap<K, V> {
	private Map<K, V> map = new ConcurrentHashMap<K, V>();
	private Map<K, V> parentMap;
	private Bean bean;
	private Class<K> keyClass;
	private Class<V> valueClass;
	private FilterExpression filterExp;

	/**
	 * To be called by DB implementation
	 * 
	 * @param keyClass
	 * @param valueClass
	 */
	DBMapHashMapImpl(Class<K> keyClass, Class<V> valueClass) {
		this(keyClass, valueClass, null);
	}

	/**
	 * To be called by sub filter DBMaps
	 * 
	 * @param keyClass
	 * @param valueClass
	 * @param parentMap
	 * @throws IntrospectionException
	 */
	DBMapHashMapImpl(Class<K> keyClass, Class<V> valueClass, Map<K, V> parentMap) {
		this.keyClass = keyClass;
		this.valueClass = valueClass;
		if (parentMap == null) {
			this.parentMap = this.map;
		} else {
			this.parentMap = parentMap;
		}
		
		try {
			bean = new QueryBean(valueClass);
		} catch (IntrospectionException e) {
			throw new FilterParsingException("Enable to introspect the bean class.");
		}
	}

	@Override
	public V get(K key) {
		if (key == null) {
			throw new IllegalArgumentException("Key can not be null.");
		}
		return map.get(key);
	}

	@Override
	public void put(K key, V value) {
		if (key == null) {
			throw new IllegalArgumentException("Key can not be null.");
		}
		parentMap.put(key, value);
		map.put(key, value);
	}

	private void putInternal(K key, V value) {
		map.put(key, value);
	}

	@Override
	public Set<K> keySet() {
		return map.keySet();
	}

	@Override
	@SuppressWarnings("unchecked")
	public DBMap<K, V> filter(String jsonFilter) {
		DBMapHashMapImpl<K, V> result = new DBMapHashMapImpl<K, V>(keyClass, valueClass, this.parentMap);
		filterExp = FilterParser.parse(jsonFilter, bean);
		for (Entry<K, V> e : map.entrySet()) {
			if (filterExp.eval(e.getValue())) {
				result.putInternal(e.getKey(), e.getValue());
			}
		}
		return result;
	}

}
